/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.cezary.butler.walili.server.db;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.BlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sql.DataSource;
import pl.cezary.butler.walili.server.db.tasks.DbTask;
import pl.cezary.butler.walili.stats.DummyInstancesCounter;
import pl.cezary.butler.walili.stats.IInstancesCounter;

/**
 *
 * @author Czarek
 */
class DbExecutor extends Thread {

    private static final Logger log = Logger.getLogger(DbExecutor.class.getName());
    private boolean stopOnFail = true;
    private BlockingQueue<DbTask> kolejka;
    private String nazwa = "std";
    private final DataSource ds;
    private final IdFactory idFactory;
    private boolean fail;
    private final IInstancesCounter instance_stats = DummyInstancesCounter.getInstance();

    public DbExecutor(BlockingQueue<DbTask> k, String n, DataSource ds, IdFactory idf) {
        super(String.format("%s: %s", DbExecutor.class.getSimpleName(), n));
        kolejka = k;
        nazwa = n;
        this.ds = ds;
        idFactory = idf;
    }
    private static final int QUEUE_LENGTH_DELTA = 125;
    private static final int QUEUE_LENGTH_WARNING = 250;
    private int prevQueueLength = 0;

    private void debugQueuesLength() {
        final int queueLength = kolejka.size();
        if (queueLength > QUEUE_LENGTH_WARNING) {
            log.log(Level.WARNING, "{0}: Queue length is getting to long (currently {1} tasks in queue)", new Object[]{nazwa, queueLength});
            instance_stats.printStats();
        } else if (queueLength - prevQueueLength > QUEUE_LENGTH_DELTA) {
            log.log(Level.WARNING, "{0}: Queue length raising fast (currently {1} tasks in queue {2} more than previosly)", new Object[]{nazwa, queueLength, queueLength - prevQueueLength});
        }
        prevQueueLength = queueLength;
    }

    @Override
    public void run() {

        while (!interrupted()) {
            debugQueuesLength();
            try {
                DbTask zad = kolejka.take();
                long current = System.currentTimeMillis();
                wykonajZadanieBD_internal(zad);
                if (zad.isFail()) {
                    fail = true;
                    if (stopOnFail) {
                        log.info("Interrupting worker thread due to failed task");
                        interrupt();
                    }
                }
                log.fine(String.format("%s: Task %s done in %d ms", nazwa, zad.getClass().getName(), System.currentTimeMillis() - current));
            } catch (InterruptedException ex) {
                log.fine("Interrupted while waiting for task.");
                break;
            }
        }
        log.log(Level.INFO, "{0}: Interrupted, emptying queue ({1} tasks left).", new Object[]{nazwa, prevQueueLength});
        while (!kolejka.isEmpty()) {
            debugQueuesLength();
            DbTask zad = kolejka.poll();
            long current = System.currentTimeMillis();
            if (prevQueueLength % 10 == 0 || prevQueueLength < 10) {
                log.log(Level.INFO, "{0}: {1} tasks left next to process: {2}", new Object[]{nazwa, prevQueueLength, zad});
            }
            log.fine(String.format("%s: Task %s done in %d ms", nazwa, zad.getClass().getName(), System.currentTimeMillis() - current));
            wykonajZadanieBD_internal(zad);
        }
    }

    public BlockingQueue<DbTask> getQueue() {
        return kolejka;
    }

    private void wykonajZadanieBD_internal(DbTask zad) {
        try {
            assert (ds != null) : "Wykonywanie zadan dozwolone jest dopiero po polaczeniu";
            final Connection taskConnection = ds.getConnection();
            instance_stats.countInstance(zad);
            zad.setExecutionContext(taskConnection, idFactory);
            zad.run();
            taskConnection.close();
        } catch (SQLException ex) {
            log.log(Level.SEVERE, "Sql error while trying to execute task", ex);
        }

    }

    boolean isQueueEmpty() {
        return kolejka.isEmpty();
    }
}
